
#pragma once

/// ----------------------------------------------------------------------------
/// <summary>
/// Provide generic/general support for map files.  Some functionality is
/// provided to read-write to the shared resource, still user may use the 'base'
/// address to read-write directly to the mapfile as if it is RAM.  Still,
/// remember to call 'Flush()' to dump contents to file if data should be saved.
/// </summary>
/// <file>Win32.IO.MapFile.h</file>
/// <author>Eduardo Sobrino</author>
/// <sample>
///
///    Kif::Win32::IO::MapFile<char> mapFile;
///    if (mapFile.Open("mapFilePath",mapFileSize))
///    {
///       TCHAR *pData = mapFile.GetBuffer();
///       while (*pData)
///       {
///          // use pData ...
///       }
///       mapFile.Close() ;  // release resources
///    }
///
/// </sample>
/// <date>May/98</date>

#include "Win32.String.h"
#include "Win32.Diagnostics.Result.h"
#include "Win32.Memory.h"
#include "Win32.IO.File.h"
#include "Win32.IO.TextFile.h"
#include "Win32.MemoryBuffer.h"

namespace Kif
{
namespace Win32
{
namespace IO
{

static const TCHAR DEFAULT_ALIAS[] = TEXT("mapfile") ;

template <typename T>
class MapFile
{
private:

   // --------------------------------------------------------------------------

   Kif::Win32::Diagnostics::Result lastResult;

   String  fileName;            // map file, file name
   HANDLE  fileHandle ;         // file handle
   HANDLE  mapHandle ;          // map file / view handle

   T      *mapBaseAddress ;     // address base
   String  alias ;              // shared name (if any)
   DWORD   maxSize ;            // mapfile max-size

   /// <summary>Init mapfile (shared-memory) object</summary>
   /// <date>May/98  (ESob)</date>
   void Reset()
   {
      lastResult.Clear();
      mapBaseAddress = NULL ;
      alias.Copy((TCHAR*)DEFAULT_ALIAS,SizeOf(DEFAULT_ALIAS)) ;
      maxSize    = 0 ;
      mapHandle  = 0 ;
      fileHandle = 0 ;
   }  // end of reset map file object

public:

   inline Kif::Win32::Diagnostics::Result *GetLastResult()
   {
      return(&lastResult);
   }  // end of GetLastResult

   /// <summary>Init mapfile (shared-memory) object</summary>
   /// <date>May/98  (ESob)</date> 
   MapFile()
   {
      Reset() ;
   }  // end of init map file object

   /// <summary>Release allocated resources</summary>
   /// <date>May/98  (ESob)</date> 
  ~MapFile()
   {
      Close() ;
   }  // end of init map file object

   // --------------------------------------------------------------------------

   T *GetBuffer()
   {
      return(mapBaseAddress);
   }  // end of GetBuffer

   /// <summary>Set the map file alias for sharing purpuses</summary>
   /// <param name="filePath">file path name</param>
   /// <param name="filePathLength">file path string length</param>
   /// <param name="maxMapFileSize">maximum map file length</param>
   /// <returns>true is returned if all is ok, else the lastResult will
   /// contain the last error/s</returns>
   /// <date>May/98 (ESob)</date>
   bool Open(TCHAR *filePath, size_t filePathLength, DWORD maxMapFileSize)
   {
      if (mapBaseAddress != NULL)
         Close();
      SetFilePath(filePath,filePathLength);
      SetMaxSize(maxMapFileSize);
      return(OpenReadWrite());
   }  // end of Open map-file

   bool OpenReadOnly(TCHAR *filePath, size_t filePathLength, DWORD maxMapFileSize)
   {
      if (mapBaseAddress != NULL)
         Close();
      SetFilePath(filePath,filePathLength);
      SetMaxSize(maxMapFileSize);
      return(OpenReadOnly());
   }  // end of Open map-file

   bool Open(TCHAR *filePath, DWORD maxMapFileSize)
   {
      return(
         Open(filePath,Kif::Win32::String::GetLength(filePath)),maxMapFileSize);
   }  // end of Open map-file

   bool Open(String *filePath, DWORD maxMapFileSize)
   {
      if (filePath == NULL)
         return(false);
      return(Open(filePath->GetBuffer(),filePath->GetLength(),maxMapFileSize));
   }  // end of Open map-file

   bool Open(String &filePath, DWORD maxMapFileSize)
   {
      return(Open(filePath.GetBuffer(),filePath.GetLength(),maxMapFileSize));
   }  // end of Open map-file

   bool Open(TCHAR *filePath)
   {
      DWORD fileSize;
      Kif::Win32::IO::File fl;
      fileSize = fl.FileSize(filePath);
      return Open(filePath,Kif::Win32::String::GetLength(filePath),fileSize);
   }  // end of Open

   bool OpenReadOnly(TCHAR *filePath)
   {
      DWORD fileSize;
      Kif::Win32::IO::File fl;
      fileSize = fl.FileSize(filePath);
      return (OpenReadOnly(
         filePath,Kif::Win32::String::GetLength(filePath),fileSize));
   }  // end of Open

   /// <summary>Set the map file maximum size</summary>
   /// <date>May/98  (ESob)</date>
   void SetMaxSize(DWORD maxSize)
   {
      this->maxSize = maxSize;
   }  // end of SetMaxSize

   /// <summary>Get buffer size</summary>
   /// <date>May/98  (ESob)</date>
   DWORD GetBufferSize()
   {
      return(maxSize);
   }  // end of GetBufferSize

   /// <summary>Set the map file alias for sharing purpuses</summary>
   /// <param name="newAlias">new alias</param>
   /// <param name="stringLength">string length</param>
   /// <date>May/98 (ESob)</date>
   inline void SetAlias(TCHAR *newAlias, size_t stringLength)
   {
      alias.Copy(newAlias,stringLength);
   }  // end of SetAlias

   inline void SetAlias(Kif::Win32::String *newAlias)
   {
      if (newAlias == NULL)
         return;
      alias.Copy(newAlias->GetBuffer(),newAlias->GetLength());
   }

   inline void SetAlias(Kif::Win32::String &newAlias)
   {
      alias.Copy(newAlias.GetBuffer(),newAlias.GetLength());
   }

   /// <summary>Set the map file - file path</summary>
   /// <param name="filePath">file path name</param>
   /// <param name="stringLength">string length</param>
   /// <date>May/98  (ESob)</date>
   void SetFilePath(TCHAR *filePath, size_t stringLength)
   {
      fileName.Copy(filePath, stringLength);
   }  // end of SetFilePath

   void SetFilePath(Kif::Win32::String *filePath)
   {
      if (filePath == NULL)
         return;
      fileName.Copy(filePath->GetBuffer(), filePath->GetLength());
   }

   void SetFilePath(Kif::Win32::String &filePath)
   {
      fileName.Copy(filePath.GetBuffer(), filePath.GetLength());
   }

   /// <summary>Flush contents of map file</summary>
   /// <date>May/98  (ESob)</date>
   inline bool Flush()
   {
      bool status = false;
      if (mapBaseAddress) {
         if (!FlushViewOfFile(mapBaseAddress,maxSize))
            lastResult.ErrorNo = GetLastError();
         else
            status = true;
      }

      return(status) ;
   }  // end of flush mapped view

   /// <summary>Create map file</summary>
   /// <param name="filePath">file path name</param>
   /// <param name="maxSize">maximum size of buffer</param>
   /// <date>May/98  (ESob)</date> 
   bool Create(TCHAR *filePath, DWORD maxSize)
   {
      lastResult.Clear();

      Kif::Win32::IO::TextFile tfile;
      if (!tfile.OpenReadWrite(filePath))
      {
         lastResult = *tfile.GetLastResult();
         return(false);
      }

      int c ;
      for (c = 0 ; c < maxSize ; c++)
         tfile.Write((T*)" ",sizeof(T)) ;
      tfile.Close();

      return(true) ;
   }  // end of create file

   /// <summary>Create map file</summary>
   /// <param name="filePath">file path name</param>
   /// <param name="bytes">bytes to save to file</param>
   /// <param name="bytesLength">number of bytes to write</param>
   /// <date>May/98  (ESob)</date> 
   static DWORD Create(TCHAR *filePath, BYTE *bytes, DWORD bytesLength)
   {
      Kif::Win32::IO::TextFile tfile;
      if (!tfile.OpenReadWrite(filePath))
         return(0);

      DWORD bytesWritten = tfile.Write(bytes,bytesLength) ;
      tfile.Close();

      return(bytesWritten) ;
   }  // end of create file

   /// <summary>Open map file</summary>
   /// <param name="dwDesiredAccess">desired access</paarm>
   /// <param name="dwShareMode">shared mode</paarm>
   /// <date>May/98  (ESob)</date> 
   bool Open(
      DWORD dwDesiredAccess,
      DWORD dwShareMode)
   {
      bool status;
      lastResult.Clear();

      if (alias.GetLength() == 0)
         alias.Copy((TCHAR*)DEFAULT_ALIAS,SizeOf(DEFAULT_ALIAS)) ;

      // try to open physical file first...

      fileHandle = CreateFile(
         fileName.GetBuffer(),
         dwDesiredAccess,                     // desired access
         dwShareMode,                         // shared access
         0, OPEN_EXISTING, 0, 0) ;

      if (fileHandle == INVALID_HANDLE_VALUE)
      {
         lastResult.ErrorNo = GetLastError();
         status = false;
      }
      else
      {
         DWORD dwProtect =
            dwDesiredAccess == GENERIC_READ ? PAGE_READONLY : PAGE_READWRITE;

         // create the mapping
         mapHandle = CreateFileMapping(
            fileHandle, 0,
            dwProtect,      // specify map file access
            0, 0, alias.GetBuffer()) ;

         if (mapHandle <= 0)
         {
            lastResult.ErrorNo = GetLastError();
            status = false;
         }
         else
         {
            DWORD dwAccess =
               dwProtect == PAGE_READONLY ? FILE_MAP_READ : FILE_MAP_WRITE;
            mapBaseAddress = (T*)MapViewOfFile(mapHandle, dwAccess,
               0,0,0) ;
            status = true;
         }
      }

      return(status) ;
   }  // end of open map file for read and/or write as specified

   /// <summary>Open map file for read only</summary>
   /// <date>May/98  (ESob)</date> 
   bool OpenReadOnly()
   {
      return(Open(GENERIC_READ, FILE_SHARE_READ));
   }  // end of open map file for read only

   /// <summary>Open map file for read and write</summary>
   /// <date>May/98  (ESob)</date> 
   bool OpenReadWrite()
   {
      return(Open(GENERIC_READ | GENERIC_WRITE,
         FILE_SHARE_READ | FILE_SHARE_WRITE));
   }  // end of open map file for read and write

   /// <summary>Close map file and associated view</summary>
   /// <date>May/98  (ESob)</date>
   inline void Close()
   {
      lastResult.Clear();
      if (mapBaseAddress)
      {
         if (!UnmapViewOfFile(mapBaseAddress))
            lastResult.ErrorNo = GetLastError();
      }

      CloseHandle(mapHandle);
      CloseHandle(fileHandle);
      Reset();
   }  // end of close map file


   /// <summary>Write given buffer to map file</summary>
   /// <param name="buffer">buffer to write from (char*)</param>
   /// <param name="bufferLength">buffer length (DWORD)</param>
   /// <returns>Returns the number of bytes written</returns>
   /// <date>May/98  (ESob)</date> 
   inline DWORD Write(T *buffer, DWORD bufferLength)
   {
      if (mapBaseAddress) {
         Kif::Win32::Memory::copy<T>(
            mapBaseAddress,maxSize,buffer,bufferLength);
         return(bufferLength) ;
      }
      else
         return(0) ;
   }  // end of write to file

   /// <summary>Read buffer from map file</summary>
   /// <param name="buffer">buffer to write too (char*)</param>
   /// <param name="bufferLength">buffer length (DWORD)</param>
   /// <date>May/98  (ESob)</date> 
   inline DWORD Read(T *buffer, DWORD bufferLength)
   {
      if (mapBaseAddress)
      {
         Kif::Win32::Memory::copy<T>(
            buffer,bufferLength,mapBaseAddress,maxSize);
         return(maxSize) ;
      }
      else
         return(0) ;
   }  // end of read from file

   // --------------------------------------------------------------------------

   /// <summary>Read map file buffer and return the buffer as a MemoryBuffer
   /// </summary>
   /// <param name="filePath">file path</param>
   /// <param name="filePathLength">file path length</param>
   /// <param name="mapFileSize">map file size</param>
   /// <returns>Returns the MemoryBuffer else NULL</returns>
   /// <date>May/98  (ESob)</date>
   static MemoryBuffer<T> *GetMemoryBuffer(
      TCHAR *filePath, size_t filePathLength, DWORD mapFileSize)
   {
      // see if we have been given the mapFileSize else try to calc it...

      Kif::Win32::IO::File fl;
      if (mapFileSize <= 0)
         mapFileSize = fl.FileSize(filePath);
      if (mapFileSize <= 0)
         return(NULL);

      // get the requested memory buffer instance...
      Kif::Win32::MemoryBuffer<T> *mb = new Kif::Win32::MemoryBuffer<T>();
      if (mb == NULL)
         return(NULL);

      // allocate memory for map file contents...
      mb->Alloc(mapFileSize+1);
      *(mb->Buffer + mapFileSize) = NULL;

      // open the memory file to copy
      MapFile<T> mf;
      if (mf.OpenReadOnly(filePath,filePathLength,mapFileSize))
      {
         T *sourceBuff = mf.GetBuffer();
         *mb->Buffer = NULL;
         Kif::Win32::Memory::copy(mb->Buffer,mapFileSize,sourceBuff,mapFileSize);
         *(mb->Buffer+mapFileSize) = NULL;
         mf.Close();
      }
      else
      {
         delete mb;
         return(NULL);
      }

      return(mb);
   }  // end of GetMemoryBuffer

   // --------------------------------------------------------------------------

   static MemoryBuffer<typename T> *GetMemoryBuffer(TCHAR *filePath, DWORD mapFileSize)
   {
      return(GetMemoryBuffer(filePath,
         Kif::Win32::String::GetLength(filePath),mapFileSize));
   }  // end of GetMemoryBuffer
   static MemoryBuffer<typename T> *GetMemoryBuffer(TCHAR *filePath)
   {
      return(GetMemoryBuffer(filePath,
         Kif::Win32::String::GetLength(filePath),0));
   }  // end of GetMemoryBuffer

}  ;  // end of MapFile

// provide commonly use type definition
typedef MapFile<char> MapAnsiStringFile;
typedef MapFile<wchar_t> MapUnicodeFile;

}  // end of Kif::Win32::IO
}  // end of Kif::Win32
}  // end of Kif


